import { Injectable } from '@nestjs/common';
import { CreateMenuDto } from './dto/create-menu.dto';
import { UpdateMenuDto } from './dto/update-menu.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { MenuEntity } from './entities/menu.entity';
import { Brackets, Repository } from 'typeorm';
import { BusinessException } from 'src/common/filter/business-exception/business.exception';
import { UserEntity } from 'src/user/entities/user.entity';

@Injectable()
export class MenuService {
  constructor(
    @InjectRepository(MenuEntity)
    private readonly menuRepository: Repository<MenuEntity>,
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
  ) {}
  // 验证用户权限
  async validUser(
    user: UserEntity,
    valid: boolean = true,
  ): Promise<UserEntity | null> {
    const res_user = await this.userRepository.findOne({
      where: { id: user.id },
    });
    if (valid && (!res_user || res_user.is_admin !== 1))
      throw new BusinessException('无操作权限');
    return res_user;
  }
  // 验证菜单是否重名
  async validExist(dto: CreateMenuDto | UpdateMenuDto, id?: string) {
    if (!dto.name) return;
    const exist = await this.menuRepository.findOne({
      where: {
        parent_id: dto.parent_id,
        name: dto.name,
        is_admin: dto.is_admin ?? 0,
        is_delete: 0,
      },
    });
    if (exist && (!id || exist.id !== id))
      throw new BusinessException('菜单名已存在');
  }
  // 验证上级菜单是否有效
  async validParent(dto: CreateMenuDto | UpdateMenuDto): Promise<number> {
    let level = 1;
    if (dto.parent_id !== '') {
      const parent = await this.menuRepository.findOne({
        where: {
          id: dto.parent_id,
          is_admin: dto.is_admin ?? 0,
          is_delete: 0,
        },
      });
      if (!parent) throw new BusinessException('未找到上级菜单');
      level = parent.level + 1;
    }
    return level;
  }
  async create(createMenuDto: CreateMenuDto, user: UserEntity) {
    // 验证用户权限
    await this.validUser(user);
    // 验证同级菜单是否有同名
    await this.validExist(createMenuDto);
    // 验证上级菜单
    const level = await this.validParent(createMenuDto);
    const sort = await this.menuRepository.count();
    return await this.menuRepository.save({ ...createMenuDto, level, sort });
  }

  async findAll(user: UserEntity) {
    const res_user = await this.validUser(user, false);
    const list = await this.menuRepository
      .createQueryBuilder()
      .where({ is_delete: 0 })
      .andWhere(
        new Brackets((qb) => {
          if (res_user === null || res_user.is_admin !== 1) {
            return qb.where('is_admin = :isadmin and is_enable = :isenable', {
              isadmin: 0,
              isenable: 1,
            });
          }
          return qb;
        }),
      )
      .orderBy('sort', 'ASC')
      .getMany();
    return list;
  }

  findOne(id: number) {
    return `This action returns a #${id} menu`;
  }

  async update(id: string, updateMenuDto: UpdateMenuDto, user: UserEntity) {
    // 验证用户权限
    await this.validUser(user);
    // 找到更新的菜单
    const updateMenu = await this.menuRepository.findOne({ where: { id } });
    if (!updateMenu) throw new BusinessException('未找到菜单');
    if (!updateMenuDto.name || updateMenuDto.name === '')
      updateMenuDto.name = updateMenu.name;
    // 更新上级菜单
    if (
      updateMenuDto.parent_id &&
      updateMenuDto.parent_id !== updateMenu.parent_id
    ) {
      // 验证上级菜单
      const level = await this.validParent(updateMenuDto);
      // 验证同级菜单是否有同名
      await this.validExist(updateMenuDto);
      updateMenu.level = level;
      updateMenu.parent_id = updateMenuDto.parent_id ?? '';
      // TODO 如果存在子集，则需要更新子集的level
    } else if (updateMenuDto.name && updateMenuDto.name !== updateMenu.name) {
      // 验证同级菜单是否有同名
      await this.validExist(updateMenuDto, updateMenu.id);
    }
    updateMenu.name = updateMenuDto.name;
    if (updateMenuDto.icon) updateMenu.icon = updateMenuDto.icon;
    if (updateMenuDto.path) updateMenu.path = updateMenuDto.path;

    return this.menuRepository.save(updateMenu);
  }

  remove(id: number) {
    return `This action removes a #${id} menu`;
  }
}
